package Twitim::XMPP;

use strict;
use warnings;

use AnyEvent;
use base qw(Twitim::NetBase);

my $Enable = 0;

if (   ( eval 'use Net::XMPP2::Client; 1;' ? 1 : 0 )
    && ( eval 'use XML::Simple; 1;' ? 1 : 0 ) )
{
    $Enable = 1;
}

my $Message_Count = 0;
my @Twitter_Cmd = (
    [ 'invite', '_invite' ],
    [ 'follow', 'fo_llow' ],
    [ 'get',    '_get' ],
    [ 'fav',    '_fav' ],
    [ 'whois',  '_whois' ],
    [ 'track',  '_track' ],
    [ 'stats',  '_stats' ],
    [ 'on',     'o_n' ],
    [ 'off',    '_off' ],
    [ 'help',   '_help' ]
);

sub new {
    my $this  = shift;
    my $class = ref($this) || $this;
    my $self = {
        dest_jid => 'twitter@twitter.com',
        app_name => 'Twitim',
        protocol => 'XMPP',
        is_valid => 0,
        @_,
    };
    bless $self, $class;

    $self->{'cb'}->{'connect'}        ||= sub { };
    $self->{'cb'}->{'disconnect'}     ||= sub { };
    $self->{'cb'}->{'session_status'} ||= sub { };
    $self->{'cb'}->{'message'}        ||= sub { $self->_message(@_) };
    $self->{'cb'}->{'error'}          ||= sub { $self->_error(@_)};

    $self->{'cb'}->{'get_current_tab'} ||= sub { return undef };
    $self->{'cb'}->{'get_opened_tab'}  ||= sub { return [] };

    $self->_setup;
    $self->connect;
    return $self;
}

sub connect {
    my $self = shift;

    return if !$Enable;

    # initialize
    delete $self->{'timer'};
    $self->{'is_valid'} = 0;
    $self->{'client'}->remove_accounts;
    $self->set_error();
    $self->set_error( undef, 'XMPP session' );

    return if !$self->{'userid'} || !$self->{'password'};

    $self->{'is_valid'} = 1;
    $self->{'cb'}->{'connect'}->();
    $self->{'cb'}->{'session_status'}->( $self->_l10n("XMPP Connecting...") );

    $self->{'client'}
        ->add_account( $self->{'userid'}, $self->{'password'}, undef, undef,
        { resource => $self->{'app_name'} } );
    Twitim::NetBase->set_my_id( $self->{'userid'} );
}

sub update {
    my ( $self, $string ) = @_;
    my $to_jid = $self->{'dest_jid'};
    $self->{'client'}->send_message( $string => $to_jid, undef, 'chat' );
}

sub is_valid {
    my $self = shift;
    return $self->{is_valid};
}

sub is_enable {
    return $Enable;
}

sub get_message_count {
    my $self   = shift;
    my $result = $Message_Count;

    $Message_Count = 0; # reset counter
    return $result;
}

sub get_cmd_array {
    my $self = shift;
    return @Twitter_Cmd;
}

sub _setup {
    my $self = shift;

    return if !$Enable;

    $self->{'client'} = Net::XMPP2::Client->new;

    $self->{'client'}->reg_cb(
        session_ready => sub { $self->_session_ready },
        disconnect => sub {
            my ( $cl, $acc, $h, $p, $reas ) = @_;
            my $msg = $self->_l10n("Disconnect");
            $msg .= ": $reas" if defined $reas;
            $self->set_error($msg);
            $self->{'cb'}->{'disconnect'}->();
        },
        error => sub {
            my ( $cl, $acc, $err ) = @_;
            $self->set_error( $err->string );
        },
        presence_update => sub {
            my ( $cl, $acc, $ro, $contact, $old_presence, $new_presence ) = @_;

            return if $contact->jid ne $self->{'dest_jid'};

            $self->{'presence'} = $new_presence
                ? $new_presence->show || 'available'
                : '';

            my $status
                = $self->{'presence'}
                ? undef
                : sprintf( $self->_l10n("%s is offline"), $self->{'dest_jid'} );
            $self->set_error($status);

            # printf "%s: %s\n", $contact->jid, $self->{'presence'};
        },
        message_xml => sub {
            my ( $cl, $acc, $node ) = @_;
            $self->_message_xml( $cl, $acc, $node );
        },
    );

    $self->{'client'}->start;
}

sub _session_ready {
    my $self = shift;

    $self->{'cb'}->{'session_status'}->( $self->_l10n("XMPP Session ready") );
    $self->set_error();

    $self->{'timer'} = AnyEvent->timer(
        after => 10,
        cb    => sub {
            $self->{'cb'}->{'session_status'}->();
            if ( !$self->{'error'} && !$self->{'presence'} ) {
                $self->set_error(
                    sprintf(
                        $self->_l10n("%s is offline"), $self->{'dest_jid'}
                    ),
                    'XMPP session'
                );
                $self->{'timer'} = AnyEvent->timer(
                    after => 30,
                    cb    => sub { $self->set_error( undef, 'XMPP session' ) }
                );
            }
        }
    );
}

sub _message_xml {
    my ( $self, $cl, $acc, $node ) = @_;
    my ( $msg_id, $tw_msg_id, $tab_name, $direct );

    my $xml      = XML::Simple->new();
    my $msg_data = $xml->XMLin( $node->as_string() );

    # print Dumper($msg_data) . "\n";
    # print $msg_data->{'entry'}{'updated'} ."\n";

    return if ( $msg_data->{'type'} && $msg_data->{'type'} eq 'error' );

    # jid (twitter user name)
    ( my $jid = $msg_data->{'from'} ) =~ s|(.*)/.*|$1|;

    # body
    my $body;
    if ( ref( $msg_data->{'body'} ) eq "HASH" ) {
        $body = $msg_data->{'body'}->{'content'};
    }
    else {
        $body = $msg_data->{'body'};
    }
    return if !$body;
    utf8::upgrade($body);
    chomp $body;

    if (   $jid eq $self->{'dest_jid'}
        && $body =~ /^Oops! Your update was over 140 characters./ )
    {
        print "Oops! Your update was over 140 characters.";
        return;
    }

    if ( $jid eq $self->{'dest_jid'} ) {
        if ( $body =~ m/^(Direct from \S+):(.*?)$/s ) {
            ( $jid, $body ) = ( $1, $2 );
            $direct = 1;
            Twitim::NetBase->set_icon( $jid,
                $msg_data->{'profile_image'}{'content'} );
        }
        elsif ( $body =~ m/^(\S+): (.*?)$/s && $1 ne 'followers' ) {

            # 'followers' is for stats command
            ( $jid, $body ) = ( $1, $2 );
            Twitim::NetBase->set_icon( $jid,
                $msg_data->{'entry'}{'source'}{'icon'} );
            Twitim::NetBase->set_username( $jid,
                $msg_data->{'entry'}{'source'}{'author'}{'name'} );
        }
    }

    # message id
    if ( $msg_data->{'entry'}{'id'} ) {
        $msg_id = $msg_data->{'entry'}{'id'};
        $msg_id =~ s|.*/statuses/(.*)$|$1|;

        # timeline counter
        my $my_id = Twitim::NetBase->get_my_id;
        if ( $jid !~ /^($my_id|\(.*\)|.*\@.*)$/ ) {
            $Message_Count++;
        }
    }
    elsif ( $msg_data->{'id'} && $msg_data->{'id'} =~ /^tw_/
        && !defined $direct ) # replies from twitter system
    {
        $tab_name = $self->{'cb'}->{'get_current_tab'};
    }

    # time string
    my $time_str = $msg_data->{'entry'}{'updated'};
    $time_str = $self->get_unixtime($time_str);

    # output
    $self->{'cb'}->{'message'}->(
        {   user_id    => $jid,
            body       => $body,
            message_id => $msg_id,
            unixtime   => $time_str,
            output     => $tab_name
        }
    );

    # output for Direct Messages Tab
    if (defined $direct && grep /^Direct Messages$/,
        $self->{'cb'}->{'get_opened_tabs'}
        )
    {
        $jid  =~ s/^Direct from //;
        $body =~ s/^\n//;
        $body =~ s/\n\nReply with .*?$//;

        $self->{'cb'}->{'message'}->(
            {   user_id    => $jid,
                body       => $body,
                message_id => $msg_id,
                output     => 'Direct Messages'
            }
        );
    }
}

1;
